#!/bin/bash

# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

. "$(dirname "$0")/common.sh" || exit 1
. "${SRC_ROOT}/platform/dev/toolchain_utils.sh"

# Script must run inside the chroot
restart_in_chroot_if_needed "$@"

assert_not_root_user

# Developer-visible flags.
DEFINE_string board "$DEFAULT_BOARD" \
  "The name of the board to set up."
DEFINE_boolean default $FLAGS_FALSE \
  "Set board to the default board in your chroot"
DEFINE_boolean force $FLAGS_FALSE \
  "Force re-creating board root."
DEFINE_boolean usepkg $FLAGS_TRUE \
  "Use binary packages to bootstrap."

FLAGS_HELP="usage: $(basename $0) [flags]

setup_board sets up the sysroot for a particular board. This script is called
automatically when you run build_packages, so there is typically no need to
call it directly, unless you want to blow away your board (using --force).
"
show_help_if_requested "$@"

# The following options are advanced options, only available to those willing
# to read the source code. They are not shown in help output, since they are
# not needed for the typical developer workflow.
DEFINE_string accept_licenses "" \
  "Licenses to append to the accept list."
DEFINE_string board_overlay "" \
  "Location of the board overlay."
DEFINE_integer jobs -1 \
  "How many packages to build in parallel at maximum."
DEFINE_boolean latest_toolchain $FLAGS_FALSE \
  "Use the latest ebuild for all toolchain packages (gcc, binutils, libc, \
kernel). This overrides the other toolchain version options."
DEFINE_string profile "" \
  "The portage configuration profile to use. Profile must be located in overlay-board/profiles"
DEFINE_boolean quiet $FLAGS_FALSE \
  "Don't print warnings when board already exists."
DEFINE_boolean skip_toolchain_update $FLAGS_FALSE \
  "Don't update toolchain automatically."
DEFINE_boolean skip_chroot_upgrade $FLAGS_FALSE \
  "Don't run the chroot upgrade automatically; use with care."
DEFINE_string toolchain "" \
  "Toolchain.  For example: i686-pc-linux-gnu, armv7a-softfloat-linux-gnueabi"
DEFINE_string variant "" \
  "Board variant."
DEFINE_boolean regen_configs ${FLAGS_FALSE} \
  "Regenerate all config files (useful for modifying profiles w/out rebuild)."
DEFINE_boolean chrome_binhost_only $FLAGS_FALSE \
  "Only fetch packages from the Chrome binhost."
DEFINE_boolean skip_board_pkg_init ${FLAGS_FALSE} \
  "Don't emerge any packages during setup_board into the board root."
DEFINE_string board_root "" \
  "Board root."
DEFINE_string brick "" "Brick to set up."

# The --reuse_pkgs_from_local_boards flag tells Portage to share binary
# packages between boards that are built locally, so that the total time
# required to build several boards is reduced. This flag is only useful
# when you are not able to use remote binary packages, since remote binary
# packages are usually more up to date than anything you have locally.
DEFINE_boolean reuse_pkgs_from_local_boards $FLAGS_FALSE \
  "Bootstrap from local packages instead of remote packages."

# Parse command line flags
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# Only now can we die on error.  shflags functions leak non-zero error codes,
# so will die prematurely if 'switch_to_strict_mode' is specified before now.
switch_to_strict_mode

if [[ -z "${FLAGS_board}" ]] && [[ -z "${FLAGS_brick}" ]]; then
  die "--board or --brick required."
elif [[ -n "${FLAGS_brick}" ]]; then
  # Clear the board value if brick was provided (backward compatibility).
  [[ -z "${FLAGS_board}" ]] || warn "--brick was used, ignoring --board value"
  FLAGS_board=
fi

get_board_and_variant "${FLAGS_board}" "${FLAGS_variant}" "${FLAGS_brick}"

# Before we can run any tools, we need to update chroot
UPDATE_ARGS="--toolchain_boards=${BOARD}"
if [ "${FLAGS_usepkg}" -eq "${FLAGS_TRUE}" ]; then
  UPDATE_ARGS+=" --usepkg"
else
  UPDATE_ARGS+=" --nousepkg"
fi
if [[ "${FLAGS_jobs}" -ne -1 ]]; then
  UPDATE_ARGS+=" --jobs=${FLAGS_jobs}"
fi
if [[ "${FLAGS_skip_toolchain_update}" -eq "${FLAGS_TRUE}" ]] \
  || [[ -n "${FLAGS_brick}" ]]; then
  UPDATE_ARGS+=" --skip_toolchain_update"
fi
if [ "${FLAGS_skip_chroot_upgrade}" -eq "${FLAGS_FALSE}" ] ; then
  "${SRC_ROOT}/scripts"/update_chroot ${UPDATE_ARGS}
fi

case "$BOARD" in
  *-host)
    if [[ $FLAGS_usepkg -eq $FLAGS_TRUE ]]; then
      die_notrace "host boards only support --nousepkg"
    fi
    HOST_BOARD=true
    ;;
  *)
    HOST_BOARD=false
esac
# Locations we will need
BOARD_ROOT="${FLAGS_board_root:-/build/${BOARD_VARIANT}}"
CROSSDEV_OVERLAY="/usr/local/portage/crossdev"
CHROMIUMOS_OVERLAY="/usr/local/portage/chromiumos"
CHROMIUMOS_CONFIG="${CHROMIUMOS_OVERLAY}/chromeos/config"
CHROMIUMOS_PROFILES="${CHROMIUMOS_OVERLAY}/profiles"
BOARD_ETC="${BOARD_ROOT}/etc"
BOARD_SETUP="${BOARD_ETC}/make.conf.board_setup"
BOARD_PROFILE="${BOARD_ETC}/portage/profile"

eval $(portageq envvar -v CHOST PKGDIR)

SYSROOT_EXISTS=false
if [ -d "${BOARD_ROOT}" ]; then
  if [[ ${FLAGS_force} -eq ${FLAGS_TRUE} ]]; then
    echo "--force set.  Re-creating ${BOARD_ROOT}..."
    # Removal takes long. Make it asynchronous.
    TEMP_DIR=`mktemp -d`
    sudo mv "${BOARD_ROOT}" "${TEMP_DIR}"
    sudo rm -rf --one-file-system "${TEMP_DIR}" &
  else
    # The sysroot exists. Take note so that we can exit early once the
    # configuration has been updated.
    SYSROOT_EXISTS=true
  fi
else
  # Regenerating configs w/out a board root doesn't make sense.
  FLAGS_regen_configs=${FLAGS_FALSE}
fi

# Setup the user specific profile.
if [[ ! -s /etc/make.conf.user ]]; then
  sudo_clobber /etc/make.conf.user <<\EOF
# This file is useful for doing global (chroot and all board) changes.
# Tweak emerge settings, ebuild env, etc...
#
# Make sure to append variables unless you really want to clobber all
# existing settings.  e.g. You most likely want:
#   FEATURES="${FEATURES} ..."
#   USE="${USE} foo"
# and *not*:
#   USE="foo"
#
# This also is a good place to setup ACCEPT_LICENSE.
EOF
fi

# Setup the make.confs. We use the following:
#    make.conf             <- Overall target make.conf [arm, x86, etc. version]
#    make.conf.board_setup <- Declares CHOST, ROOT, etc.
#    make.conf.board       <- Optional board-supplied make.conf.
#    make.conf.user        <- User specified parameters.
cmds=(
  "mkdir -p '${BOARD_ROOT}' '${BOARD_ETC}' '${BOARD_PROFILE}' /usr/local/bin"
  "ln -sf /etc/make.conf.user '${BOARD_ROOT}/etc/make.conf.user'"
  "mkdir -p '${BOARD_ROOT}/etc/portage/hooks'"
)
for d in "${SCRIPTS_DIR}"/hooks/*; do
  cmds+=( "ln -sfT '${d}' '${BOARD_ROOT}/etc/portage/hooks/${d##*/}'" )
done
sudo_multi "${cmds[@]}"

# Generating the standard configuration file (make.conf.board_setup) for the
# sysroot.
if [[ -n "${FLAGS_brick}" ]]; then
  cros_sysroot_utils generate-config --sysroot="${BOARD_ROOT}" \
    --brick="${FLAGS_brick}" --out-file="${BOARD_SETUP}"
else
  cros_sysroot_utils generate-config --sysroot="${BOARD_ROOT}" \
    --board="${FLAGS_board}" --out-file="${BOARD_SETUP}"
fi

# Generate wrappers for portage helpers (equery, portageq, emerge, etc...).
# Those are used to generate make.conf.board.
cros_sysroot_utils create-wrappers --sysroot="${BOARD_ROOT}" \
  --friendlyname="${BOARD_VARIANT}"

# Select the profile to build based on the board and profile passed to
# setup_board.  The developer can later change profiles by running
# cros_choose_profile manually.
if ! cros_choose_profile --profile "${FLAGS_profile}" \
      --board_root "${BOARD_ROOT}"; then
  sudo rm -rf --one-file-system "${BOARD_ROOT}"
  die "Selecting profile failed, removing incomplete board directory!"
fi

if ${HOST_BOARD}; then
  cmds=(
    "ln -sf '${CHROMIUMOS_CONFIG}/make.conf.${BOARD}' \
      '${BOARD_ETC}/make.conf'"
    "cp -f '/etc/make.conf.host_setup' '${BOARD_ETC}/'"

    # Setting up symlinks for bootstrapping multilib.
    # See http://crosbug.com/14498
    "mkdir -p '${BOARD_ROOT}'{/usr,}/lib64"
    "ln -sfT lib64 '${BOARD_ROOT}/lib'"
    "rm -r '${BOARD_ROOT}/usr/lib'"
    "ln -sfT lib64 '${BOARD_ROOT}/usr/lib'"

    # Copying some files for bootstrapping empty chroot.
    # See http://crosbug.com/14499
    "mkdir -p '${BOARD_ETC}'/{init.d,xml}"
    "cp /etc/xml/catalog '${BOARD_ETC}'/xml/"
    "cp /etc/init.d/functions.sh '${BOARD_ETC}'/init.d/"
  )
  sudo_multi "${cmds[@]}"

  EMERGE_CMD="${GCLIENT_ROOT}/chromite/bin/parallel_emerge"
  PACKAGES="system virtual/target-sdk world"

  # First, rebuild all packages from scratch. This is needed to make sure
  # we rebuild all chroot packages.
  sudo -E $EMERGE_CMD --emptytree --with-bdeps=y $PACKAGES
  sudo eclean -d packages

  # Next, install our rebuilt packages into our separate root.
  HOST_FLAGS="--root=$BOARD_ROOT --update --verbose --deep --root-deps"
  HOST_FLAGS+=" --with-bdeps=y --newuse --jobs=$NUM_JOBS --usepkgonly"
  sudo -E $EMERGE_CMD $HOST_FLAGS --select=y $PACKAGES
  sudo cp -a "${PKGDIR}" $BOARD_ROOT/packages

  # Copy our chroot version into the newly packaged chroot.
  sudo cp -a "${CHROOT_VERSION_FILE}" "${BOARD_ROOT}${CHROOT_VERSION_FILE}"

  # Now cleanup paths referencing the ROOT from the *.la files.
  sudo find $BOARD_ROOT -type f -name '*.la' | xargs sudo \
      sed -i -e "s|$BOARD_ROOT/|/|g"
else
  conf="${CHROMIUMOS_CONFIG}/make.conf.generic-target"
  sudo ln -sf "${conf}" "${BOARD_ETC}/make.conf"
  EXTRA_ARGS=()
  if [[ -n "${FLAGS_accept_licenses}" ]]; then
    EXTRA_ARGS+=( --accepted-licenses="${FLAGS_accept_licenses}" )
  fi
  sudo "${GCLIENT_ROOT}/chromite/bin/cros_sysroot_utils" generate-make-conf \
    --sysroot="${BOARD_ROOT}" --out-file="${BOARD_ETC}/make.conf.board" \
    "${EXTRA_ARGS[@]}"

  # We need to create make.conf.board in two steps as we need make.conf.board to
  # exist before we can generate the binhost list.
  EXTRA_ARGS=()
  if [[ ${FLAGS_chrome_binhost_only} -eq ${FLAGS_TRUE} ]]; then
    EXTRA_ARGS+=( --chrome-only )
  fi
  if [[ ${FLAGS_reuse_pkgs_from_local_boards} -eq ${FLAGS_TRUE} ]]; then
    EXTRA_ARGS+=( --local-only )
  fi
  cros_sysroot_utils generate-binhosts --sysroot="${BOARD_ROOT}" \
    "${EXTRA_ARGS[@]}" | sudo_append $BOARD_ETC/make.conf.board

  # We install the toolchain related bits after the BOARD_ROOT, BOARD_PROFILE
  # and BOARD_ETC directories have been created.
  if [[ ${FLAGS_regen_configs} -eq ${FLAGS_FALSE} ]]; then
    if [[ ${FLAGS_skip_board_pkg_init} -eq ${FLAGS_FALSE} ]]; then
      "${GCLIENT_ROOT}/src/scripts/install_toolchain" --sysroot="${BOARD_ROOT}"
    fi

    # If the sysroot already existed and we are not regenerating configuration,
    # exit early.
    if ${SYSROOT_EXISTS}; then
      if [[ ${FLAGS_quiet} -eq ${FLAGS_FALSE} ]]; then
        warn "Board output directory '$BOARD_ROOT' already exists."
        warn "Exiting early."
        warn "Use --force to clobber the board root and start again."
      fi
      exit 0
    fi
    if [[ ${FLAGS_skip_board_pkg_init} -eq ${FLAGS_FALSE} ]]; then
      # Emerge the kernel headers into the board build root.  Use rdeps to
      # avoid pulling any spurious DEPEND things in that we don't care about.
      BASE_EMERGE_FLAGS="--select --quiet --root-deps=rdeps"
      if [[ "${FLAGS_usepkg}" -eq "${FLAGS_TRUE}" ||
            "${FLAGS_reuse_pkgs_from_local_boards}" -eq "${FLAGS_TRUE}" ]]; then
        BASE_EMERGE_FLAGS+=" --getbinpkg --usepkg"
      fi

      sudo -E "emerge-${BOARD_VARIANT}" ${BASE_EMERGE_FLAGS} \
        sys-kernel/linux-headers sys-libs/gcc-libs
      unset BASE_EMERGE_FLAGS
    fi
  fi
fi

if [ $FLAGS_default -eq $FLAGS_TRUE ] ; then
  echo $BOARD_VARIANT > "$GCLIENT_ROOT/src/scripts/.default_board"
fi

command_completed
echo "Done!"
echo "The SYSROOT is: ${BOARD_ROOT}"

if [[ -n ${FLAGS_board_root} ]]; then
  # cros_workon do not support board_root.
  exit 0
fi
